/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.util;

import java.util.*;
/*
 *
 * @author Daniel Quest
 * @date   Sat October 17, 2009
 */

public class SequencePosition implements Comparable, MappedPosition {
    private int start;
    private int end;
    private char strand; // +, -, .
    private double weight = 0;
    private String queryID = "";   //QueryID and subjectID are any arbitrary string.
    private String subjectID = "";

    public SequencePosition(){
        init();
    }

    public SequencePosition(int s, int e, char stnd){
        set(s,e,stnd);
    }

    public SequencePosition(int s, int e, char stnd, double weight){
        set(s,e,stnd);
        this.weight = weight;
    }

    public SequencePosition(String s){
        set(s);
    }

    public double getWeight(){ return weight; }
    public void setWeight(Double weight){ this.weight = weight; }

    public void setEnd(int end) {
        this.end = end;
    }

    public void setStart(int start) {
        this.start = start;
    }

    private SequencePosition init(){
        start = 1;
        end = 2;
        strand = '+';
        return this;
    }

    public int getStart(){  return start;  }
    public int getEnd(){  return end;  }
    public char getStrand(){  return strand;  }
    public void setStrand(char c){
        if(strand == '+' || strand == '-' || strand == '.'){
            strand = c;
        }
    }
    public void set(int s, int e, char stnd){
        start = s;
        end = e;
        strand = stnd;
        if (checkOrder() == 0){
            flipStartAndEnd();
        }
        return;
    }

    public String createEMBL(){
        Integer s = new Integer(start);
        Integer e = new Integer(end);
        String pos = "";
        if(strand == '-') pos = "complement(";
        pos = pos + s.toString();
        pos = pos + "..";
        pos = pos + e.toString();
        if(strand == '-') pos += ")";
        return pos;
    }

    /**
     * set assumes an embl style string 12345..12567
     * @param s
     */
    public LinkedList set(String indxs){
        LinkedList ll = new LinkedList();
        setPositions(indxs, ll, '+');
        return ll;
    }

    public void flipStartAndEnd(){
        int temp = end;
        end = start;
        start = temp;
    }

    public void flipStartEndStrand(){
        flipStartAndEnd();
        if (strand == '+') strand = '-';
        else // (strand == '-' )
            strand = '+';
    }

    /**
     * return 0 if out of order
     * return 1 if order is fine (start<end and strand tells us orentation)
     */
    public int checkOrder(){
        if (start > end) return 0;
        else return 1;
    }

    public int compareTo(Object o){
        SequencePosition cop = (SequencePosition) o;
        if (cop.checkOrder() == 0 ){
            cop.flipStartEndStrand();
        }
        if (this.checkOrder() == 0){
            this.flipStartEndStrand();
        }
        if (cop.start == start && cop.end == end ){ // && cop.strand == strand){ NOTE: This assumes that if they have same indexes, they are the same sequence position regardless of strand
            return 0; //equal intervals
        }else if (cop.start < start || cop.end < end)
            return -1;
        else
            return 1;
    }

    public SequencePosition deepCopy(){
        SequencePosition f = new SequencePosition();
        f.start = start;
        f.end = end;
        f.strand = strand;
        return f;
    }

    public int length(){
        int length = 0;
        if (strand == '+'){
            if(end > 0 && start > 0){
                return end - start + 1;
            }else{
                System.err.println("Error: SequencePosition.length() is returning zero because of negative index");
                System.exit(0);
                return 0;
            }
        }else{
            if(start > end){
                return start - end + 1;
            }
            else return end - start + 1;
        }
    }

    /**
     * contains:
     * input: calling object, o,  and an additional sequence position f.
     * if f is contained by o e.g. o.start > f.start and f.end < o.end, true
     * else false;
     *
     */
    
    /**
     * overlaps returns true if the SequencePostions overlap, otherwise false
     * overlap is defined as an absolute position overlap regardless of strand
     * ie. all the nodes are as if they where and interval graph.
     */
    public boolean overlaps(SequencePosition f){
        boolean rval=false;
        int temp=0;
        int tstart = this.start;
        int tend = this.end;
        int fstart = f.start;
        int fend = f.end;
        if (f.checkOrder() == 0 ){
            f.flipStartEndStrand();
        }
        if (this.checkOrder() == 0){
            this.flipStartEndStrand();
        }
        
        if(fstart<tstart&&fend<tstart){ rval=false;
        }else if(tstart<fstart&&tend<fstart){ rval=false;            
        }else if(fstart<=tend){ rval=true;
        }else if(tstart<=fend){ rval=true;
        }else if(tstart<=fstart&&fend<=tend){ rval=true;
        }else if(fstart<=tstart&&tend<=fend){ rval=true;
        }
        return rval;
            /***********t***************/
                 /*************f*************/
            //if(tstart <= fstart && tend >= fstart) return true;
                  /***********t***************/
            /*************f*************/
            //if(tstart >= fstart && fend <= tend) return true;
        //return false;
    }

    /**
     * overlapNumber returns the number of positions overlapped between two SequencePositions
     */
    public int overlapNumber(SequencePosition f){
        int rval = 0;
        int temp=0;
        int tstart = this.start;
        int tend = this.end;
        int fstart = f.start;
        int fend = f.end;
        if (f.checkOrder() == 0 ){
            f.flipStartEndStrand();
        }
        if (this.checkOrder() == 0){
            this.flipStartEndStrand();
        }
                                        /***********t***************/
        /*************f*************/
        if(fend < tstart){
            return 0;
        /***********t***************/
                                      /*************f*************/
        }else if(tend < fstart){
            return 0;
        /***********t***************/
        /*************f*************/
        }else if (fstart == tstart && fend == tend) return fend - fstart +1;


        if(fstart<tstart&&fend<tstart){ rval=0;
        }else if(tstart<fstart&&tend<fstart){ rval=0;
        }else if(fstart<=tend&& tend<fend){ rval=tend-fstart;
        }else if(tstart<=fend&& fend<tend){ rval=fend-tstart;
        }else if(tstart<=fstart&&fend<=tend){ rval=fend-fstart;
        }else if(fstart<=tstart&&tend<=fend){ rval=tend-tstart;
        }else if(tend==fstart){ rval=1;
        }else if(tstart==fend){ rval=1;
        }else if(tend==fend&&tstart<fstart){ rval=fend-fstart;
        }else if(tstart==fend&&fstart<tstart){ rval=fend-tstart;
        }else if(tstart==fstart&&fend<tend){ rval=fend-fstart;
        }else if(tstart==fstart&&tend<fend){ rval=tend-tstart;
        }
        return rval+1;
    }


    /**
     * Recursive function that handles a set of positions.
     * Examples Include:
     * 255845..256171
     * complement(255845..256171)
     * join(107525..108133,110763..111563)
     * complement(join(255845..256171,256171..256449))
     * order(107525..108133,110763..111563)
     * complement(order(255845..256171,256171..256449))
     * @param indxs ToDo
     * @param ll ToDo
     * @param stnd A character representation of the DNA strand (+/-)
     * @return
     */
    public LinkedList setPositions(String indxs, LinkedList ll, char stnd){
        Integer s = new Integer(0);
        Integer e = new Integer(0);
        String dotdot = "\\.\\.";
        //indxs = indxs.trim();
        //System.err.println(indxs);
        if(indxs.matches("join\\(.*\\)")){
            //Remove join( ... )
            indxs = indxs.replaceFirst("join\\(", "");
            indxs = indxs.replaceFirst("\\)", "");
            ll = setPositions(indxs, ll, stnd);
            return ll;
        }else if( indxs.matches("complement\\(.*\\)") ){
            //Remove complement(
            indxs = indxs.replaceFirst("complement\\(", "");
            indxs = indxs.replaceFirst("\\)", "");
            stnd = '-';
            ll = setPositions(indxs, ll, stnd);
            return ll;
        }else if(indxs.matches("^\\d+..\\d+,\\d+..\\d+.*")){  // more than one ,
            int commaPos = indxs.indexOf(',');
            //System.err.println(indxs.substring(0, commaPos));
            //System.err.println(indxs.substring(commaPos+1));
            ll = setPositions(indxs.substring(0, commaPos), ll, stnd);
            ll = setPositions(indxs.substring(commaPos+1), ll, stnd);
            return ll;
        }else if(indxs.matches("^\\d+\\.\\.\\d+")){  //only one
            //System.err.println("Parsing the following string (SequencePosition.set(String): " + indxs);
            String[] digits = indxs.split(dotdot);
            int a = s.parseInt(digits[0]);
            int b = e.parseInt(digits[1]);
            char c = stnd;
            SequencePosition sp = new SequencePosition(a,b,c);
            ll.add(sp);
            return ll;
        }else{
            System.err.println("The following string could not be parsed(SequencePosition.set(String): " + indxs);
            System.exit(0);
            return ll;
        }
    }

    public String getQueryID() {
        return this.queryID;
    }

    public String getSubjectID() {
        return this.subjectID;
    }

    public void setQueryID(String q) {
        this.queryID = q;
    }

    public void setSubjectID(String s) {
        this.subjectID = s;
    }

}

